//<<<+++OPENSOURCE
//<<<+++OPENSOURCE_LICENSE
//<<<+++OPENSOURCE_BEGIN LIC==FOXIT||LIC==GOOGLE

/**
 * @addtogroup FXCRT
 * @{
 */

/**
 * @file
 * @brief FOXIT SIMPLE XML PARSER
 *
 * Implements a very simple XML parser, with the following limitations:
 * - Only UTF-8 encoding is supported.
 * - No validation is done.
 * - Tag names and attribute names are within ASCII or ISO8859-1 charset.
 *
 * This simple XML parser is intended to be used within limited context, like XFA form specification
 * in PDF file, or XML-based data communication between Foxit network components. 
 * Foxit will develop another more sophisicated XML parser for more general usage.
 */
//<<<+++OPENSOURCE_MUST_BEGIN
#ifndef _FX_XML_H_
#define _FX_XML_H_
//<<<+++OPENSOURCE_MUST_END

#ifdef _FXM_OPENSOURCE_
	//<<<+++OPENSOURCE_MUST_BEGIN LIC==GOOGLE
	#ifndef _FX_BASIC_H_
		#include "fx_basic.h"
	#endif
	//<<<+++OPENSOURCE_MUST_END
#else
	//<<<+++OPENSOURCE_MUST_BEGIN LIC==FOXIT
	#ifndef _FX_PROCESS_H_
		#include "fx_process.h"
	#endif
	//<<<+++OPENSOURCE_MUST_END
#endif

class CXML_AttrItem : public CFX_Object
{
public:
#ifndef _FXM_OPENSOURCE_
	//<<<+++OPENSOURCE_BEGIN LIC==FOXIT
	CFX_ByteStringL	m_QSpaceName;
	CFX_ByteStringL	m_AttrName;
	CFX_WideStringL	m_Value;
	
	void	Empty(IFX_Allocator* pAllocator = NULL)
	{
		m_QSpaceName.Empty(pAllocator);
		m_AttrName.Empty(pAllocator);
		m_Value.Empty(pAllocator);
	}

	//<<<+++OPENSOURCE_END
#else
	//<<<+++OPENSOURCE_BEGIN LIC==GOOGLE
	CFX_ByteString	m_QSpaceName;
	CFX_ByteString	m_AttrName;
	CFX_WideString	m_Value;
	//<<<+++OPENSOURCE_END
#endif
};

class CXML_AttrMap : public CFX_Object
{
public:
	CXML_AttrMap() { m_pMap = NULL; }
#ifndef _FXM_OPENSOURCE_
	//<<<+++OPENSOURCE_BEGIN LIC==FOXIT
	~CXML_AttrMap() { RemoveAll(NULL); }

	const CFX_WideStringL*	Lookup(FX_BSTR space, FX_BSTR name) const;
	void					SetAt(FX_BSTR space, FX_BSTR name, FX_WSTR value, IFX_Allocator* pAllocator = NULL);
	void					RemoveAt(FX_BSTR space, FX_BSTR name, IFX_Allocator* pAllocator = NULL);
	void					RemoveAll(IFX_Allocator* pAllocator = NULL);
	//<<<+++OPENSOURCE_END
#else
	//<<<+++OPENSOURCE_BEGIN LIC==GOOGLE
	~CXML_AttrMap() { RemoveAll(); }

	const CFX_WideString*	Lookup(FX_BSTR space, FX_BSTR name) const;
	void					SetAt(FX_BSTR space, FX_BSTR name, FX_WSTR value);
	void					RemoveAt(FX_BSTR space, FX_BSTR name);
	void					RemoveAll();
	//<<<+++OPENSOURCE_END
#endif
	int						GetSize() const;
	CXML_AttrItem&			GetAt(int index) const;

	CFX_ObjectArray<CXML_AttrItem>*	m_pMap;
};

class CXML_Content : public CFX_Object
{
public:
	CXML_Content() : m_bCDATA(FALSE), m_Content() {}
#ifndef _FXM_OPENSOURCE_
	//<<<+++OPENSOURCE_BEGIN LIC==FOXIT
	~CXML_Content() {Empty(NULL);}

	void	Empty(IFX_Allocator* pAllocator = NULL)
	{
		m_Content.Empty(pAllocator);
	}

	void	Set(FX_BOOL bCDATA, FX_WSTR content, IFX_Allocator* pAllocator = NULL)
	{
		m_bCDATA = bCDATA;
		m_Content.Set(content, pAllocator);
	}

	FX_BOOL			m_bCDATA;
	CFX_WideStringL	m_Content;
	//<<<+++OPENSOURCE_END
#else
	//<<<+++OPENSOURCE_BEGIN LIC==GOOGLE
	void	Set(FX_BOOL bCDATA, FX_WSTR content)
	{
		m_bCDATA = bCDATA;
		m_Content = content;
	}

	FX_BOOL			m_bCDATA;
	CFX_WideString	m_Content;
	//<<<+++OPENSOURCE_END
#endif
};

/**
 * @brief XML element.
 */
class CXML_Element : public CFX_Object
{
public:
#ifndef _FXM_OPENSOURCE_
	//<<<+++OPENSOURCE_BEGIN LIC==FOXIT
	/**
	 * Construct an XML element using data in specified buffer.
	 * The buffer can be discarded immediately after construction finished, because at this time,
	 * all data have been loaded into the element (and its descendant element).
	 * 
	 * @param[in] pBuffer			The input buffer.
	 * @param[in] size				The size in bytes of the input buffer.
	 * @param[in] bSaveSpaceChars	Indicates whether need save space characters in content string, TRUE if save, or FALSE.
	 * @param[out] pParsedSize		(Optional) Receiving the parsed size.
	 * @param[in] pAllocator		Allocator used in this class. NULL is to use default allocator.
	 * @return An XML element.
	 */
	static CXML_Element*	Parse(const void* pBuffer, size_t size, FX_BOOL bSaveSpaceChars = FALSE, FX_FILESIZE* pParsedSize = NULL, IFX_Allocator* pAllocator = NULL);

	/**
	 * Parse XML contents from an IFX_FileRead stream object.
	 * The IFX_FileRead object can be discarded immediately after construction finished, because at this time,
	 * all data have been loaded into the element (and its descendant element).
	 * 
	 * @param[in] pFile				Stream object to be parsed.
	 * @param[in] bSaveSpaceChars	Indicates whether need save space characters in content string, TRUE if save, or FALSE.
	 * @param[out] pParsedSize		(Optional) Receiving the parsed size.
	 * @param[in] pAllocator		Allocator used in this class. NULL is to use default allocator.
	 * @return An XML element.
	 */
	static CXML_Element*	Parse(IFX_FileRead *pFile, FX_BOOL bSaveSpaceChars = FALSE, FX_FILESIZE* pParsedSize = NULL, IFX_Allocator* pAllocator = NULL);

	/**
	 * Parse XML contents from an IFX_BufferRead stream object.
	 * The IFX_BufferRead object can be discarded immediately after construction finished, because at this time,
	 * all data have been loaded into the element (and its descendant element).
	 * 
	 * @param[in] pBuffer			Stream object to be parsed.
	 * @param[in] bSaveSpaceChars	Indicates whether need save space characters in content string, TRUE if save, or FALSE.
	 * @param[out] pParsedSize		(Optional) Receiving the parsed size.
	 * @param[in] pAllocator		Allocator used in this class. NULL is to use default allocator.
	 * @return An XML element.
	 */
	static CXML_Element*	Parse(IFX_BufferRead *pBuffer, FX_BOOL bSaveSpaceChars = FALSE, FX_FILESIZE* pParsedSize = NULL, IFX_Allocator* pAllocator = NULL);

	/**
	 * Construct an empty element, with a qualified namespace and tag name.
	 *
	 * @param[in] qSpace			Qualified namespace, or empty if no namespace or default namespace is used.
	 * @param[in] tagName			The input tag name.
	 * @param[in] pAllocator		Allocator used in this class. NULL is to use default allocator.
	 */
	CXML_Element(FX_BSTR qSpace, FX_BSTR tagName, IFX_Allocator* pAllocator = NULL);

	/**
	 * Construct an empty element, with qualified tag name.
	 *
	 * @param[in] qTagName			Qualified tag name.
	 * @param[in] pAllocator		Allocator used in this class. NULL is to use default allocator.
	 */
	CXML_Element(FX_BSTR qTagName, IFX_Allocator* pAllocator = NULL);

	/**
	 * Construct an empty element.
	 * 
	 * @param[in] pAllocator		Allocator used in this class. NULL is to use default allocator.
	 */
	CXML_Element(IFX_Allocator* pAllocator = NULL);
	//<<<+++OPENSOURCE_END
#else
	//<<<+++OPENSOURCE_BEGIN LIC==GOOGLE
	static CXML_Element*	Parse(const void* pBuffer, size_t size, FX_BOOL bSaveSpaceChars = FALSE, FX_FILESIZE* pParsedSize = NULL);
	static CXML_Element*	Parse(IFX_FileRead *pFile, FX_BOOL bSaveSpaceChars = FALSE, FX_FILESIZE* pParsedSize = NULL);
	static CXML_Element*	Parse(IFX_BufferRead *pBuffer, FX_BOOL bSaveSpaceChars = FALSE, FX_FILESIZE* pParsedSize = NULL);
	CXML_Element(FX_BSTR qSpace, FX_BSTR tagName);
	CXML_Element(FX_BSTR qTagName);
	CXML_Element();
	//<<<+++OPENSOURCE_END
#endif
	/** The destructor. */
	~CXML_Element();

	/**
	 * Empty the current element.
	 */
	void	Empty();

#ifndef _FXM_OPENSOURCE_
//<<<+++OPENSOURCE_MUST_BEGIN LIC==FOXIT
	/**
	 * Clone the current element.
	 *
	 * @param[in] pAllocator		Allocator used in Cloned element. NULL is to use default allocator.
	 */
	CXML_Element*	Clone(IFX_Allocator* pAllocator = NULL);
//<<<+++OPENSOURCE_MUST_END
#endif

	/**
	 * @name Fetching data from a parsed element
	 */
	 /*@{*/

	/**
	 * Get the tag name of the element.
	 *
	 * @param[in] bQualified	Indicates whether return a qualified tag name or not.
	 * @return	If bQualified is TRUE, returns tag name with qualified namespace, or only local tag name.
	 */
	CFX_ByteString			GetTagName(FX_BOOL bQualified = FALSE) const;
#ifndef _FXM_OPENSOURCE_
	//<<<+++OPENSOURCE_BEGIN LIC==FOXIT
	void					GetTagName(CFX_ByteStringL &tagName, FX_BOOL bQualified = FALSE) const;
	//<<<+++OPENSOURCE_END
#endif
	/**
	 * Get the namespace of the element tag.
	 * 
	 * @param[in] bQualified	Indicates whether return a qualified namespace or full URI namespace.
	 * @return	If bQualified is TRUE, returns qualified namespace value, or full URI value.
	 */
	CFX_ByteString			GetNamespace(FX_BOOL bQualified = FALSE) const;
#ifndef _FXM_OPENSOURCE_
	//<<<+++OPENSOURCE_BEGIN LIC==FOXIT
	void					GetNamespace(CFX_ByteStringL &nameSpace, FX_BOOL bQualified = FALSE) const;
	//<<<+++OPENSOURCE_END
#endif
	/**
	 * Get the full URI value for a qualified namespace.
	 *
	 * @param[in] qName		Qualified namespace.
	 * @return	The full URI value. If name is empty, the default namespace URI is returned.
	 */
	CFX_ByteString			GetNamespaceURI(FX_BSTR qName) const;
#ifndef _FXM_OPENSOURCE_
	//<<<+++OPENSOURCE_BEGIN LIC==FOXIT
	void					GetNamespaceURI(FX_BSTR qName, CFX_ByteStringL &uri) const;
	//<<<+++OPENSOURCE_END
#endif
	/**
	 * Get the parent element of the element.
	 *
	 * @return the parent element pointer.
	 */
	CXML_Element*			GetParent() const {return m_pParent;}

	/**
	 * Get the count of attributes of the element.
	 *
	 * @return the count of attributes.
	 */
	FX_DWORD				CountAttrs() const {return m_AttrMap.GetSize();}

	/**
	 * Get the attribute and move to next position.
	 *
	 * @param[in] index			Index of the attribute (start from 0).
	 * @param[out] space		It receives the qualified namespace of attribute name.
	 * @param[out] name			It receives the attribute name (local name).
	 * @param[out] value		It receive the attribute value.
	 */
	void					GetAttrByIndex(int index, CFX_ByteString &space, CFX_ByteString &name, CFX_WideString &value) const;
#ifndef _FXM_OPENSOURCE_
	//<<<+++OPENSOURCE_BEGIN LIC==FOXIT
	void					GetAttrByIndex(int index, CFX_ByteStringL &space, CFX_ByteStringL &name, CFX_WideStringL &value) const;
	//<<<+++OPENSOURCE_END
#endif
	/**
 	 * Determine whether a qualified attribute exists or not.
 	 *
	 * @param[in] qName			qualified attribute name.
	 * @return		TRUE if attribute exists, FALSE if doesn't exist.
	 */
	FX_BOOL					HasAttr(FX_BSTR qName) const;

	/**
	 * Get attribute value without a namespace.
	 *
	 * @param[in] name			qualified attribute name.
	 * @param[out] attribute	It receive the attribute value, A UTF-16LE wide string.
	 * @return the attribute exist or not.
	 */
	FX_BOOL					GetAttrValue(FX_BSTR name, CFX_WideString& attribute) const;
	CFX_WideString			GetAttrValue(FX_BSTR name) const
	{ CFX_WideString attr; GetAttrValue(name, attr); return attr; }
#ifndef _FXM_OPENSOURCE_
	//<<<+++OPENSOURCE_BEGIN LIC==FOXIT
	const CFX_WideStringL*	GetAttrValuePtr(FX_BSTR name) const;
	//<<<+++OPENSOURCE_END
#endif

	/**
	 * Get attribute value. Encoded in UTF-16LE format.
	 *
	 * @param[in] space			qualified namespace.
	 * @param[out] attribute	It receive the attribute value, A UTF-16LE wide string.
	 * @return the attribute exist or not.
	 */
	FX_BOOL					GetAttrValue(FX_BSTR space, FX_BSTR name, CFX_WideString& attribute) const;
	CFX_WideString			GetAttrValue(FX_BSTR space, FX_BSTR name) const
	{ CFX_WideString attr; GetAttrValue(space, name, attr); return attr; }
#ifndef _FXM_OPENSOURCE_
	//<<<+++OPENSOURCE_BEGIN LIC==FOXIT
	const CFX_WideStringL*	GetAttrValuePtr(FX_BSTR space, FX_BSTR name) const;
	//<<<+++OPENSOURCE_END
#endif

	/**
	 * Get an integer from a particular attribute without a namespace.
	 *
	 * @param[in] name			qualified attribute name.
	 * @param[out] attribute	It receive the attribute value, A integer value.
	 * @return the attribute exist or not.
	 */
	FX_BOOL					GetAttrInteger(FX_BSTR name, int& attribute) const;
	int						GetAttrInteger(FX_BSTR name) const
	{ int attr = 0; GetAttrInteger(name, attr); return attr; }

	/**
	 * Get an integer from a particular attribute, using a namespace.
	 *
	 * @param[in] space			qualified namespace.
	 * @param[in] name			Local name for the attribute.
	 * @param[out] attribute	It receive the attribute value, A integer value.
	 * @return the attribute exist or not.
	 */
	FX_BOOL					GetAttrInteger(FX_BSTR space, FX_BSTR name, int& attribute) const;
	int						GetAttrInteger(FX_BSTR space, FX_BSTR name) const
	{ int attr = 0; GetAttrInteger(space, name, attr); return attr; }

	/**
	 * Get an integer from a particular attribute without a namespace.
	 *
	 * @param[in] name			qualified attribute name.
	 * @param[out] attribute	It receive the attribute value, A float value.
	 * @return the attribute exist or not.
	 */
	FX_BOOL					GetAttrFloat(FX_BSTR name, FX_FLOAT& attribute) const;
	FX_FLOAT				GetAttrFloat(FX_BSTR name) const
	{ FX_FLOAT attr = 0; GetAttrFloat(name, attr); return attr; }

	/**
	 * Get an integer from a particular attribute, using a namespace.
	 *
	 * @param[in] space			qualified namespace.
	 * @param[in] name			Local name for the attribute.
	 * @param[out] attribute	It receive the attribute value, A float value.
	 * @return the attribute exist or not.
	 */
	FX_BOOL					GetAttrFloat(FX_BSTR space, FX_BSTR name, FX_FLOAT& attribute) const;
	FX_FLOAT				GetAttrFloat(FX_BSTR space, FX_BSTR name) const
	{ FX_FLOAT attr = 0; GetAttrFloat(space, name, attr); return attr; }

	/**
	 * Get number of children of this element, including content segments and sub-elements.
	 *
	 * @return The number of children.
	 */
	FX_DWORD				CountChildren() const;

	/** @brief Children Type of CXML_Element. */
	enum ChildType { Invalid, Element, Content};
	/**
	 * Get type of a child, it can be either a content segment, or a sub-element.
	 *
	 * @param[in] index		Specifies the zero-based index in children array.
	 * @return The type of specified child.
	 */
	ChildType				GetChildType(FX_DWORD index) const;

	/**
	 * Get a content segment. If this child is an element, empty will be returned.
	 *
	 * @param[in] index		Specifies the zero-based index in children array.
	 * @return A content segment.
	 */
	CFX_WideString			GetContent(FX_DWORD index) const;
#ifndef _FXM_OPENSOURCE_
	//<<<+++OPENSOURCE_BEGIN LIC==FOXIT
	const CFX_WideStringL*	GetContentPtr(FX_DWORD index) const;
	//<<<+++OPENSOURCE_END
#endif
	/**
	 * Get a particular child element. If this child is not an element, NULL will be returned.
	 *
	 * @param[in] index		Specifies the zero-based index in children array.
	 * @return A child element.
	 */
	CXML_Element*			GetElement(FX_DWORD index) const;

	/**
	 * Get an element with particular tag. If more than one element with the same tag, only the first one is returned.
	 *
	 * @param[in] space		qualified namespace.
	 * @param[in] tag		Local name for the tag.
	 * @return An element.
	 */
	CXML_Element*			GetElement(FX_BSTR space, FX_BSTR tag) const {return GetElement(space, tag, 0);}

	/**
	 * Count number of elements with particular tag.
	 *
	 * @param[in] space		qualified namespace.
	 * @param[in] tag		Local name for the tag.
	 * @return The count number of elements with the input tag.
	 */
	FX_DWORD				CountElements(FX_BSTR space, FX_BSTR tag) const;

	/**
	 * Get an element with particular tag, and specified index if more than one elements with the same tag.
	 *
	 * @param[in] space		qualified namespace.
	 * @param[in] tag		The input tag.
	 * @param[in] index		Specifies the zero-based index of element with particular tag.
	 * @return An element.
	 */
	CXML_Element*			GetElement(FX_BSTR space, FX_BSTR tag, int index) const;

	/**
	 * Find an element and return its index.
	 *
	 * @param[in] pChild	a child element pointer.
	 * @return Index number of child element, -1 if not found.
	 */
	FX_DWORD				FindElement(CXML_Element *pChild) const;

	/*@}*/ 

	/**
	 * @name Setting data to a parsed or created element
	 */
	 /*@{*/

	/**
	 * Set the tag name of the element.
	 *
	 * @param[in] qSpace		Qualified namespace for the tag.
	 * @param[in] tagname		The input tag name.
	 */
	void					SetTag(FX_BSTR qSpace, FX_BSTR tagname);

	/**
	 * Set the tag name of the element.
	 *
	 * @param[in] qTagName		Qualified tag name.
	 */
	void					SetTag(FX_BSTR qTagName);

	#ifndef _FXM_OPENSOURCE_
	//<<<+++OPENSOURCE_BEGIN LIC==FOXIT
	/**
	 * Set attribute with a wide string.
	 *
	 * @param[in] name		Qualified attribute name.
	 * @param[in] value		The input wide string.
	 */
	void					SetAttrValue(FX_BSTR name, FX_WSTR value);
	/**
	 * Set attribute with a integer.
	 *
	 * @param[in] name		Qualified attribute name.
	 * @param[in] value		The input integer.
	 */
	void					SetAttrValue(FX_BSTR name, int value);

	/**
	 * Set attribute with a float.
	 *
	 * @param[in] name		Qualified attribute name.
	 * @param[in] value		The input float.
	 */
	void					SetAttrValue(FX_BSTR name, FX_FLOAT value);
	
	/**
	 * Remove an attribute.
	 *
	 * @param[in] name		Qualified attribute name.
	 */
	void					RemoveAttr(FX_BSTR name);

	/**
	 * Add an element to the child list (at the last position).
	 * Note the child element can't be directly destroyed any more.
	 *
	 * @param[in] pElement	The input element.
	 */
	void					AddChildElement(CXML_Element* pElement);

	/**
	 * Add a content child.
	 *
	 * @param[in] content		The input content.
	 * @param[in] bCDATA		Specifies content is CDATA or not.
	 */
	void					AddChildContent(FX_WSTR content, FX_BOOL bCDATA = FALSE);

	/**
	 * Insert a child element before the specified index
	 * Note the child element can't be directly destroyed any more.
	 *
	 * @param[in] index			Specifies the zero-based index of element in the child array.
	 * @param[in] pElement		The input element.
	 */
	void					InsertChildElement(FX_DWORD index, CXML_Element* pElement);

	/**
	 * Insert a content segment before the specified index.
	 *
	 * @param[in] index			Specifies the zero-based index of element in the child array.
	 * @param[in] content		The input content.
	 * @param[in] bCDATA		Specifies content is CDATA or not.
	 */
	void					InsertChildContent(FX_DWORD index, FX_WSTR content, FX_BOOL bCDATA = FALSE);
	//<<<+++OPENSOURCE_END
	#endif

	/**
	 * Remove all children.
	 */
	void					RemoveChildren();

	/**
	 * Remove a specified child item (element or content).
	 *
	 * @param[in] index			Specifies the zero-based index of element in the child array.
	 */
	void					RemoveChild(FX_DWORD index);

	/*@}*/

	#ifndef _FXM_OPENSOURCE_
	//<<<+++OPENSOURCE_BEGIN LIC==FOXIT
	/**
	 * @name Output to a XML stream
	 */
	 /*@{*/
	
	/**
	 * Output to a XML stream.
	 * 
	 * @return A XML stream.
	 */
	CFX_ByteString			OutputStream();
	void					OutputStream(CFX_ByteStringL &xmlStream);
	
	/**
	 * Output to an IFX_FileWrite stream.
	 *
	 * @param[in] pFileWrite	An IFX_FileWrite stream object to store XML data.
	 */
	void					OutputStream(IFX_FileWrite *pFileWrite);
	//<<<+++OPENSOURCE_END
	#endif

	/*@}*/

protected:
	/** The parent element. */
	CXML_Element*			m_pParent;
#ifndef _FXM_OPENSOURCE_
	//<<<+++OPENSOURCE_BEGIN LIC==FOXIT
	/** The space name. */
	CFX_ByteStringL			m_QSpaceName;
	/** The tag name. */
	CFX_ByteStringL			m_TagName;
	//<<<+++OPENSOURCE_END
#else
	//<<<+++OPENSOURCE_BEGIN LIC==GOOGLE
	CFX_ByteString			m_QSpaceName;
	CFX_ByteString			m_TagName;
	//<<<+++OPENSOURCE_END
#endif
	/** The attributes map. */
	CXML_AttrMap			m_AttrMap;
	/** The children array. an array of CXML_Element* or CFX_WideString*. */
	CFX_PtrArray			m_Children;

	friend class CXML_Parser;
	friend class CXML_Composer;
};

//<<<+++OPENSOURCE_MUST_BEGIN
#endif	//_FX_XML_H_
//<<<+++OPENSOURCE_MUST_END

/** @} */

//<<<+++OPENSOURCE_END
